#ifndef UnstructMG_COMMON_HPP
#define UnstructMG_COMMON_HPP


#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <mpi.h>
#include <omp.h>
#include <time.h>
#ifdef __aarch64__
#include <arm_neon.h>
#endif
#include <string.h>
#include <string>
#include <vector>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <unistd.h>
#include <algorithm>

#define MIN(a, b) ((a) > (b)) ? (b) : (a)
#define MAX(a, b) ((a) > (b)) ? (a) : (b)

extern MPI_Win un_world_win;
extern void * un_world_shm;
extern int resv_tag;// reserved tag
extern int cv_tag;
extern int cm_tag;
extern int asm_tag;
extern int max_tag;// max tag allowed by MPI
void UnstructMG_Init();
void UnstructMG_Finalize();

// 非零元的坐标表达
template<typename idx_t, typename data_t, int dof>
struct COO
{
    idx_t r;
    idx_t c;
    data_t v[dof*dof];
    COO();
    COO(const COO & b);
    COO(idx_t _r, idx_t _c, data_t * _v);
    COO & operator = (const COO & b);
    bool operator == (const COO & b) const;
    bool operator < (const COO & b) const;
};
#ifdef USE_FP16
typedef COO<int, __fp16, 1> COO_I32F16_DOF1;
typedef COO<int, __fp16, 2> COO_I32F16_DOF2;
typedef COO<int, __fp16, 3> COO_I32F16_DOF3;
typedef COO<int, __fp16, 4> COO_I32F16_DOF4;
#endif
typedef COO<int, float , 1> COO_I32F32_DOF1;
typedef COO<int, float , 2> COO_I32F32_DOF2;
typedef COO<int, float , 3> COO_I32F32_DOF3;
typedef COO<int, float , 4> COO_I32F32_DOF4;
typedef COO<int, double, 1> COO_I32F64_DOF1;
typedef COO<int, double, 2> COO_I32F64_DOF2;
typedef COO<int, double, 3> COO_I32F64_DOF3;
typedef COO<int, double, 4> COO_I32F64_DOF4;

/// @brief 用于qsort的比较函数
/// @tparam T 数据类型
/// @param a 指针a
/// @param b 指针b
/// @return 比较结果
template<typename T>
int cmpfunc (const void * a, const void * b)
{
    return ( *(T*)a > *(T*)b ) ? 1 : -1;
}

template<typename idx_t, typename data_t, int dof>
void sort_indices_with_vals(const idx_t len, idx_t* inds, data_t* vals);
extern template void sort_indices_with_vals<int, __fp16, 1>(const int , int *, __fp16*);
extern template void sort_indices_with_vals<int, __fp16, 2>(const int , int *, __fp16*);
extern template void sort_indices_with_vals<int, __fp16, 3>(const int , int *, __fp16*);
extern template void sort_indices_with_vals<int, __fp16, 4>(const int , int *, __fp16*);

extern template void sort_indices_with_vals<int, float , 1>(const int , int *, float *);
extern template void sort_indices_with_vals<int, float , 2>(const int , int *, float *);
extern template void sort_indices_with_vals<int, float , 3>(const int , int *, float *);
extern template void sort_indices_with_vals<int, float , 4>(const int , int *, float *);

extern template void sort_indices_with_vals<int, double, 1>(const int , int *, double*);
extern template void sort_indices_with_vals<int, double, 2>(const int , int *, double*);
extern template void sort_indices_with_vals<int, double, 3>(const int , int *, double*);
extern template void sort_indices_with_vals<int, double, 4>(const int , int *, double*);

template<typename idx_t, typename data_t>
void sort_vals_with_indices(const idx_t len, data_t* vals, idx_t* inds);
extern template void sort_vals_with_indices(const int , float *, int *);
extern template void sort_vals_with_indices(const int , double*, int *);


/// @brief 键值对的去重
/// @tparam k_t key的数据类型
/// @tparam v_t value的数据类型
/// @param num 元素个数
/// @param key_arr key数组
/// @param val_arr value数组
/// @return 去重后的元素个数
template<typename k_t, typename v_t, int dof>
int uniquify(const int num, k_t * key_arr, v_t * val_arr = nullptr);
extern template int uniquify<int, float , 1>(const int , int *, float *);
extern template int uniquify<int, float , 2>(const int , int *, float *);
extern template int uniquify<int, float , 3>(const int , int *, float *);
extern template int uniquify<int, float , 4>(const int , int *, float *);

extern template int uniquify<int, double, 1>(const int , int *, double*);
extern template int uniquify<int, double, 2>(const int , int *, double*);
extern template int uniquify<int, double, 3>(const int , int *, double*);
extern template int uniquify<int, double, 4>(const int , int *, double*);

template<typename idx_t, typename data_t, int dof>
int uniquify(const int num, COO<idx_t, data_t, dof> * arr);
extern template int uniquify(const int , COO<int, double, 1>*);
extern template int uniquify(const int , COO<int, double, 2>*);
extern template int uniquify(const int , COO<int, double, 3>*);
extern template int uniquify(const int , COO<int, double, 4>*);
extern template int uniquify(const int , COO<int, float , 1>*);
extern template int uniquify(const int , COO<int, float , 2>*);
extern template int uniquify(const int , COO<int, float , 3>*);
extern template int uniquify(const int , COO<int, float , 4>*);

int uniq_counting(const int num, const int * arr);

#ifdef __x86_64__
extern MPI_Op MPIU_SUM_FP128;
void prod128(void* in, void* inout, int* len, MPI_Datatype* dptr);
#endif

double wall_time();

char *get_exe_path(const char *query, char *buf, int count);

char *print_int128(const __int128_t val, char *buf);
__int128_t shift_left_128 (const __int128_t val, const size_t k);
__int128_t shift_right_128 (const __int128_t val, const size_t k);
int bitone_count(__int128_t val);
void thread_load_balance(int tot_work, int num_threads, int tid, int & beg, int & end);


#endif